#include<pthread.h>
#include<stdio.h>
#include<cassert>
#include<cstdlib>
#include<vector>
#include<utility>
#include <fstream> 
#include <sstream> 
#include <string> 
#include <list>
#include "RWLock.h"

#define LECTURA 0
#define ESCRITURA 1
using namespace std;

int recurso;
int cant_threads;
int cant_op;
bool TerminoEscritura;
string readLockLog("");


sem_t mutexEscr;

RWLock lock;

vector<pair<int,int> > CountRWThread(cant_threads);  



void rVariableCompartida(unsigned int pid, int pos)
{

	lock.rlock();

	sem_wait(&mutexEscr);
	if (pos <0) {readLockLog+="[";}
	sem_post(&mutexEscr);

	
	sleep(1);

	sem_wait(&mutexEscr);
	if (pos <0) {readLockLog+="]";}
	sem_post(&mutexEscr);
	lock.runlock();
	
}

void wVariableCompartida(unsigned int pid,int pos)
{
	lock.wlock();
	
	if (pos <0) {readLockLog+="<";}
	
	
	recurso++;

	sleep(1);
	
	if (pos <0) {readLockLog+=">";}
	lock.wunlock();
}


void *EscribeNveces(void *p_minumero){
	unsigned int minumero = *((int *) p_minumero);
	int escrituras=20;
	int maxIteraciones=10000;
	while(escrituras>0){

		wVariableCompartida (minumero,-1);	
		escrituras --;
	}
	TerminoEscritura=true;
}

void *LeeNveces(void *p_minumero){
	unsigned int minumero = *((int *) p_minumero);
	int lecturas=0;
	int maxIteraciones=10000;
	while(!TerminoEscritura && lecturas<maxIteraciones){

		rVariableCompartida (minumero,-1);	
		lecturas ++;
	}

}


bool validarLocks(){
	
	int mayorMenor = 0;
	int corchete = 0;
	bool balanceado = false;
	bool cerrado = false;
	
	
	cout<< readLockLog << endl;
	    for (int i = 0; i<(readLockLog.size());i++)
	    {

		if(readLockLog[i]=='<'){
			mayorMenor++;
			if(readLockLog[i+1]=='>'){
				cerrado = true;
			}else{
				cerrado = false;
				printf("Violacion de RWLock");
				exit(1);
			}		
		}
		if(readLockLog[i]=='[')
		    corchete++;

		if(readLockLog[i]=='>')
		    mayorMenor--;
		if(readLockLog[i]==']')
		    corchete--;
	    }



	    if ((mayorMenor==0)&&(corchete==0)) balanceado = true;


	return balanceado&&cerrado;
}


void testInanicion(int cant){
	TerminoEscritura=false;
	pthread_t thread[cant];
	int tids[cant], tid;


	for (tid = 1; tid < cant; ++tid) {
        	tids[tid] = tid;
		pthread_create(&thread[tid], NULL, LeeNveces, &tids[tid]);
	}
 

	pthread_create(&thread[0], NULL, EscribeNveces, &tids[cant]); //pthread - attr - routine - arg
	
 
 
    	for (tid = 0; tid < cant; ++tid){
        	pthread_join(thread[tid], NULL);
		 
	}

	bool locks_OK = validarLocks();	
	if(locks_OK == 1){ 
		printf("Validacion de Locks: OK!\n");
	}else{ 
		printf("Validacion de Locks: FALLO!\n");
	}
	readLockLog = "";
}


int main(int argc, char* argv[])
{

	if (argc < 4) {
		printf("Faltan parametros\n");
		printf("Se espera ./test CANT_THREADS CANT_OP CANT_EJ\n");
		exit(1);
	}

	sem_init(&mutexEscr, 0, 1);
	readLockLog="";
	srand (time(NULL));

	
	RWLock lock();
	recurso = 0;
	
	
	cant_threads = atoi(argv[1]); 
	cant_op = atoi(argv[2]); 
	int cant_ejec = atoi(argv[3]); 

	cout << "Iniciando tests con " << cant_threads << " threads, " << cant_op << " operaciones, " << cant_ejec << " ejecuciones." << endl;


	cout <<"\nIniciando test inanicion"<<endl;
	testInanicion(cant_threads);
	cout <<"\nFinalizando test inanicion"<<endl;

	 	
	cout << "Todos los tests finalizaron correctamente." << endl;
    return 0;
}

